#include "symbol.h"
#include "token.h"
#include <stdlib.h>
#include <string.h>

smap *syms;
extern smap *types;

void _add_sys_type( const char *name, int size, int s_uns, int l );

void sym_init( smap *s, smap *t )
{
    syms = s;
    types = t;

    _add_sys_type( "char", 1, 1, 0 );
    _add_sys_type( "short", 2, 1, 0 );
    _add_sys_type( "int", 4, 1, 1 );
    _add_sys_type( "long", 4, 1, 1 );
    _add_sys_type( "float", 4, 0, 0 );
    _add_sys_type( "double", 8, 0, 1 );
    _add_sys_type( "void", 0, 0, 0 );
    _add_sys_type( "_BOOL", 4, 0, 0 );
    _add_sys_type( "_COMPLEX", 8, 0, 0 );
}

symbol* sym_create( symtype *ty, token *name, symbol *region )
{
    symbol *old;
    symbol *s = (symbol *)malloc( sizeof(symbol) );
    init_life( s );
    s->type = ty;
    s->name = name;
    s->region = region;
    s->ref = lt_create();
    s->beref = lt_create();
    add_ref( ty );
    add_ref( name );
    if( region )
        add_ref( region );
    if( sm_find( syms, name->name, (void **)&old ) != 0 )
    {
        s->decl = lt_create();
        lt_pushb( s->decl, s );
        sm_insert( syms, name->name, s );
        add_ref( s );
    }
    else
    {
        lt_pushb( old->decl, s );
    }
    add_ref( s );
    return s;
}

int sym_release( symbol *s )
{
    int i;
    int ref = release( s );
    if( ref <= 0 )
    {
        if( s->type )
            symtype_release( s->type );
        if( s->name )
            token_release( s->name );
        if( s->region )
            sym_release( s->region );
        for( i = 0; i < lt_size( s->ref ); ++i )
            sym_release( (symbol *)lt_get( s->ref, i ) );
        for( i = 0; i < lt_size( s->beref ); ++i )
            sym_release( (symbol *)lt_get( s->beref, i ) );
        for( i = 0; i < lt_size( s->decl ); ++i )
            sym_release( (symbol *)lt_get( s->decl, i ) );
        lt_destroy( s->beref );
        lt_destroy( s->ref );
        lt_destroy( s->decl );
        free( s );
    }
    return ref;
}

void _add_sys_type( const char *name, int size, int s_uns, int l )
{
    token *tk = token_create_raw( KEYWORD, name, "__internal_null_file__", -1, NULL, NULL );
    symtype *st = symtype_create( T_SYSTEM, NULL, 1 );
    symbol *s = sym_create( st, tk, NULL );
    vartype *vt = vt_create( T_SIMPLE, s, 0 );
    if( s_uns )
    {
        release( vt );
        vt = vt_create( T_SIMPLE, s, VTE_UNSIGNED );
        if( l )
        {
            release( vt );
            vt = vt_create( T_SIMPLE, s, VTE_UNSIGNED | VTE_BLONG );
        }
    }
    if( l )
    {
        release( vt );
        vt = vt_create( T_SIMPLE, s, VTE_BLONG );
    }
    release( tk );
    release( st );
    release( vt );
    release( s );
}

vartype* vt_find( const char *name )
{
    vartype *ty = NULL;
    sm_find( types, name, (void**)&ty );
    if( ty )
        add_ref( ty );
    return ty;
}

char* sym_name( symbol *sym, int full )
{
    static char buf[10240];
    char *name;
    if( full && sym->region )
    {
        name = sym_name( sym->region, full );
        strcpy( buf, name );
        strcat( buf, "::" );
        strcat( buf, sym->name->name );
        name = (char *)malloc( strlen(buf) + 1 );
        strcpy( name, buf );
    }
    else
    {
        name = (char *)malloc( strlen(sym->name->name) + 1 );
        strcpy( name, sym->name->name );
    }
    return name;
}

symbol* sym_find( const char *name )
{
    symbol *s = NULL;
    sm_find( syms, name, (void**)&s );
    if( s )
        add_ref( s );
    return s;
}

int sym_size( symbol *sym )
{
    return sym->type->size;
}

int sym_is_type( symbol *sym )
{
    symtype *st;
    vartype *vt;
    st = sym->type;
    switch( st->syn )
    {
    case T_STRUCT:
    case T_ENUM:
    case T_UNION:
    case T_SYSTEM:
        return 1;
    case T_VAR:
        vt = (vartype *)st->attr;
        if( vt->ext & VTE_TYPEDEF )
            return 1;
        break;
    }
    return 0;
}
